Kees Cook: 5 years with Canonical
2011, Kees Cook. This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License.
2011, Kees Cook. This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License.
dpkg-source --commit
(already presented last month). I continued some work on the hardening build flags but it s currently stalled waiting on Kees Cook to provide the required documentation to integrate in dpkg-buildflags(1).
Following a discussion held during DebConf, Michael Prokop has been kind enough to setup a git-triggered auto-builder of dpkg (using Jenkins). You can now help us by testing the latest git version. Follow those instructions:
$ wget -O - http://jenkins.grml.org/debian/C525F56752D4A654.asc sudo apt-key add - $ sudo sponge /etc/apt/sources.list.d/dpkg-git <<END deb http://jenkins.grml.org/debian dpkg main END $ sudo apt-get update && sudo apt-get upgradeOn the bug fixing side I took care of #640198 (minor man page update), #638291 (a fix to correctly handle hardlinks of conffiles), #637564 (the simplification logic of union dependencies was broken in some cases) and #631494 (interrupting dpkg-source while building a native source package left some temporary files around that should have been cleaned). WordPress update I released WordPress 3.2.1 in unstable (after having taken the time to test the updated package on my blog!) and fixed its RC bug (#625773). In the process I discovered a false positive in lintian (I reported it in 637473). Gnome-shell-timer package From time to time, I like to use the Pomodoro Technique. That s why I was an user of timer-applet in GNOME 2. Now with the switch to GNOME 3, I lost this feature. But I recently discovered gnome-shell-timer, a GNOME Shell extension that provides the same features. I created a Debian package of it and quickly filed some bugs while I was testing it (two usability issues and an encoding problem) QA Work During DebConf I met Giovanni Mascellani and he was interested to help the QA team. He started working on the backlog of bugs concerning the Package Tracking System (PTS) and submitted a bunch of patches. I reviewed them and merged them but since they were good, I quickly got lazy and got him added to the QA team so that he can commit his fixes alone. It also helps to build trust when you have had the opportunity to discuss face to face. Vacation That s not so much compared to usual but to my defense I also took 2 weeks of vacation with my family. But somehow even in vacation I can t really forget Debian. Here s my son:
3 comments Liked this article? Click here. My blog is Flattr-enabled.
dpkg-source: info: local changes detected, the modified files are: 2ping-1.1/README dpkg-source: info: you can integrate the local changes with dpkg-source --commit dpkg-source: error: aborting due to unexpected upstream changes, see /tmp/2ping_1.1-1.diff.cki8YBAs the error message hints, there s a new
--commit
command supported by dpkg-source that will generate the required quilt patch to fix this. In the processe you will have to submit a name and edit the patch header (pre-formatted with DEP3 compatible fields). You can get back the old behavior with the --auto-commit
option.
Build flags changes
Ever since we adopted the Ubuntu changes to let dpkg-buildpackage set some build related environment variables (see #465282), many Debian people expressed their concerns with this approach both because it broke some packages and because those variables are not set if you execute debian/rules directly.
In the end, the change was not quickly reverted and we fixed the package that this change broke. Despite this we later decided that the correct approach to inject build flags would be a new interface: dpkg-buildflags.
Before changing dpkg-buildpackage to no longer set the compilation flags, I wanted to ensure dpkg-buildflags had some decent coverage in the archive (to avoid breaking too many packages again). My criteria was that CDBS and dh (of debhelper) should be using it. With the recent debhelper change (see #544844) this has been reached so I changed dpkg-buildpackage accordingly.
Makefile snippets provided by dpkg
At the same time, I also wanted an easy way for maintainers not using dh or CDBS to be able to fix their package easily and go back to injecting the compilation flags in the environment but doing it from the rules files. Starting with the next version of dpkg, this will be possible with something like this:
DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/default.mkWithout DPKG_EXPORT_BUILDFLAGS the variables are not exported in the environment and have no effect unless you use them somewhere. More than build flags, this will also provide a bunch of other variables that can be useful in a rules files: all the variables provided by dpkg-architecture, vendor related variables/macro and some basic package information (mainly version related). dpkg-buildflags improvements Given the renewed importance that dpkg-buildflags will take now that dpkg-buildpackage no longer sets the corresponding environment variables, I thought that I could give it some love by fixing all the open issues and implementing some suggestions I got. I also had a chat with a few members of the technical committee to discuss how hardening build flags could be enabled in Debian and this also resulted in a few ideas of improvements. In the end, here are the main changes implemented:
--export=configure
command to inject build flags on the ./configure
command line (see commit);--dump
command that is the default (see #603435).No comment Liked this article? Click here. My blog is Flattr-enabled.
mogrify -strip spam-filtered.jpg
2011, Kees Cook. This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License.
arch/x86/mm/dump_pagetables.c
to be a loadable module so I could look at /sys/kernel/debug/kernel_page_tables
without needing to rebuild my kernel with CONFIG_X86_PTDUMP.
Comparing Lucid (2.6.32), Maverick (2.6.35), and Natty (2.6.38), it s clear to see the effects of the RO/NX improvements, especially in the Modules section which has no NX markings at all before 2.6.38:
lucid-amd64# awk '/Modules/,/End Modules/' /sys/kernel/debug/kernel_page_tables grep NX wc -l 0 maverick-amd64# awk '/Modules/,/End Modules/' /sys/kernel/debug/kernel_page_tables grep NX wc -l 0 natty-amd64# awk '/Modules/,/End Modules/' /sys/kernel/debug/kernel_page_tables grep NX wc -l 762.6.38 s memory region is much more granular, since each module has been chopped up for the various segment permissions:
lucid-amd64# awk '/Modules/,/End Modules/' /sys/kernel/debug/kernel_page_tables wc -l 53 maverick-amd64# awk '/Modules/,/End Modules/' /sys/kernel/debug/kernel_page_tables wc -l 67 natty-amd64# awk '/Modules/,/End Modules/' /sys/kernel/debug/kernel_page_tables wc -l 155For example, here s the large sunrpc module. RW is read-write, ro is read-only, x is executable, and NX is non-executable:
maverick-amd64# awk '/^'$(awk '/^sunrpc/ print $NF ' /proc/modules)'/','!/GLB/' /sys/kernel/debug/kernel_page_tables 0xffffffffa005d000-0xffffffffa0096000 228K RW GLB x pte 0xffffffffa0096000-0xffffffffa0098000 8K pte natty-amd64# awk '/^'$(awk '/^sunrpc/ print $NF ' /proc/modules)'/','!/GLB/' /sys/kernel/debug/kernel_page_tables 0xffffffffa005d000-0xffffffffa007a000 116K ro GLB x pte 0xffffffffa007a000-0xffffffffa0083000 36K ro GLB NX pte 0xffffffffa0083000-0xffffffffa0097000 80K RW GLB NX pte 0xffffffffa0097000-0xffffffffa0099000 8K pteThe latter looks a whole lot more like a proper ELF (text segment is read-only and executable, rodata segment is read-only and non-executable, and data segment is read-write and non-executable). Just another reason to make sure you re using your CPU s NX bit (via 64bit or 32bit-PAE kernels)! (And no, PAE is not slower in any meaningful way.)
CAP_SYS_PTRACE
, care of sudo.
The next most common use-case was that of crash handlers needing to do a live ptrace of a crashing program (in the rare case of Apport being insufficient). For example, KDE applications have a segfault handler that calls out to kdeinit and requests that the crash handling process be started on it, and then sits in a loop waiting to be attached to. While kdeinit is the parent of both the crashing program (debuggee) and the crash handling program (debugger), the debugger cannot attach to the debugee since they are siblings, not parent/descendant. To solve this, a prctl()
call was added so that the debugee could declare who s descendants were going to attach to it. KDE patched their segfault handler to make the prctl()
and everything Just Works again.
Breakpad, the crash handler for Firefox and Chromium, was updated to do effectively the same thing, though they had to add code to pass the process id back to the debuggee since they didn t have it handy like KDE.
Another use-case was Wine, where for emulation to work correctly, they needed to allow all Wine processes to ptrace each other to correctly emulate Windows. For this, they just declared that all descendants of the wine-server could debug a given Wine process, there-by confining their ptrace festival to just Wine programs.
One of the remaining use-cases is that of a debugging IDE that doesn t directly use ptrace itself. For example, qtcreator will launch a program and then later attach to it by launching gdb and using the attach command. This looks a lot like the crash handler use-case, except that the debuggee doesn t have any idea that it is running under an IDE. A simple solution for this is to have the IDE run its programs with the LD_PRELOAD
environment variable aimed at a short library that just calls prctl()
with the parent process id, and suddenly the IDE and its descendants (i.e. gdb) can debug the program all day long.
I ve got an example of this preloadable library written. If it turns out this is generally useful for IDEs, I could package it up like fakeroot and faketime.
/proc/PID/maps
:
0827e000-08282000 rw-p 00000000 00:00 0Knowing these, I could use gdb s find command, after attaching to the process:
0a22e000-0b08a000 rw-p 00000000 00:00 0 [heap]
efa59000-efd00000 rw-p 00000000 00:00 0
efd00000-efd21000 rw-p 00000000 00:00 0
$ gdb /some/cool/gameNo hits in the first region, but I see two hits for the account number value in the second region. Let s start there and see what s near them
(gdb) attach PID
(gdb) find /w 0 0827e000, 0 08282000, 219393
(gdb) find /w 0 0a22e000, 0 0b08a000, 219393
0xaf03d08
0xaf06ca8
(gdb) x/8x 0xaf03d08In that second hit, I see the value 0xBB8, which is 3000, and matches our account balance. Let s see what happens if we just change both of those to add a bit a few orders of magnitude above the current value
0xaf03d08: 0 00035901 0 00000000 0 00000000 0 0af06ce0
0xaf03d18: 0 0af06be0 0 00000059 0 0af03d98 0 0af041e8
(gdb) x/8x 0xaf06ca8
0xaf06ca8: 0 00035901 0 00000bb8 0 00000bb8 0 0820b148
0xaf06cb8: 0 00000001 0 00000000 0 00000000 0 00000000
(gdb) set var *0xaf06cac = 0 00100bb8And presto, clicking on the bank account details in-game shows a huge account balance of 1051576 now. No need to reverse-engineer any saved games, whew.
(gdb) set var *0xaf06cb0 = 0 00100bb8
(gdb) x/32x 0xaf06cac
0xaf06cac: 0 00100bb8 0 00100bb8 0 0820b148 0 00000001
(gdb) continue
struct thingy int magic; char data[4]; void work(char *input) char buffer[1000]; int length; struct thingy *header; header = (struct thingy *)buffer; length = strlen(input); if (length > sizeof(buffer) - sizeof(*header) - 1) abort(); strcpy(header->data, input); header->magic = 42; do_something_fun(header);The problem here is that gcc thinks that
header->data
is only 4 bytes long. But gcc doesn t know we intentionally overruled this (and even did length checking), so due to -D_FORTIFY_SOURCE=2
, the strcpy()
checks kick in when input
is more than 4 bytes.
The fix, in this case, is to use memcpy()
instead, since we actually know how long our destination is, we can replace the strcpy(...)
line with:
memcpy(header->data, input, length + 1); /* take 0-term too */This kind of header and then data stuff is common for protocol handlers. So far, things like Wine, TFTP, and others have been experiencing problems with the change. Please keep an eye out for it when doing testing.
On the 13/14 November 2010 there is a nice event in the so called Ruhrpott : OpenRheinRuhr. And I d like to invite YOU :)What should you expect from heading to the RIM in Oberhausen and paying 5 entry-fee? You get a nice program full of great talks (like mine about bley ;), mikas about OpenSource management, tokkees about Git and XTarans about cli-helpers and unknown tools), a bunch of nice people showing you their distro, software, etc (Debian is there too), a social event, a keysigning party and a lot of hacking and fun. So if you are somewhere nearby, join us and enjoy the event!
sudo module-assistant auto-install xtables-addons-source sudo iptables -p tcp ... -j TARPITThough no such thing exists for IPv6 yet. Here it is watching over the SSH port:
iptables -N INGRESS-SSH iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j INGRESS-SSH iptables -A INGRESS-SSH -p tcp --dport 22 -m state --state NEW -m recent --name SSH --set iptables -A INGRESS-SSH -p tcp --dport 22 -m state --state NEW -m recent --name SSH --update --rttl --seconds 60 --hitcount 4 -j LOG --log-prefix "[INGRESS SSH TARPIT] " iptables -A INGRESS-SSH -p tcp --dport 22 -m state --state NEW -m recent --name SSH --rcheck --rttl --seconds 60 --hitcount 4 -j TARPIT
/dev/video*
), then be sure you update your kernels for CVE-2010-2963. I ve been slowly making my way through auditing the many uses in the Linux kernel of the copy_from_user()
function, and ran into this vulnerability.
Here s the kernel code from drivers/media/video/v4l2-compat-ioctl32.c
:
static int get_microcode32(struct video_code *kp, struct video_code32 __user *up) if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) get_user(kp->datasize, &up->datasize) copy_from_user(kp->data, up->data, up->datasize)) return -EFAULT; return 0;Note that
kp->data
is being used as the target for up->data
in the final copy_from_user()
without actually verifying that kp->data
is pointing anywhere safe. Here s the caller of get_microcode32
:
static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) union struct video_tuner vt; struct video_code vc; ... karg; void __user *up = compat_ptr(arg); ... switch (cmd) ... case VIDIOCSMICROCODE: err = get_microcode32(&karg.vc, up); ...So, the contents of
up
are totally under control of the caller, and the contents of karg
(in our case, the video_code
structure) are not initialized at all. So, it seems like a call for VIDIOCSMICROCODE
would write video_code->datasize
bytes from video_code->data
into some random kernel address, just causing an Oops, since we don t control what is on the kernel s stack.
But wait, who says we can t control the contents of the kernel s stack? In fact, this compat function makes it extremely easy. Let s look back at the union. Notice the struct video_tuner
? That gets populated from the caller s up
memory via this case of the switch (cmd)
statement:
... case VIDIOCSTUNER: case VIDIOCGTUNER: err = get_video_tuner32(&karg.vt, up); ...So, to control the kernel stack, we just need to call this
ioctl
twice in a row: once to populate the stack via VIDIOCSTUNER
with the contents we want (including the future address for video_code->data
, which starts at the same location as video_tuner->name[20]
), and then again with VIDIOCSMICROCODE
.
Tricks involved here are: the definition of the VIDIOCSMICROCODE
case in the kernel is wrong, and calling the ioctl
s without any preparation can trigger other kernel work (memory faults, etc) that may destroy the stack contents. First, we need the real value for the desired case statement. This turns out to be 0 4020761b. Next, we just repeatedly call the setup ioctl
in an attempt to get incidental kernel work out of the way so that our last ioctl
doing the stack preparation will stick, and then we call the buggy ioctl
to trigger the vulnerability.
Since the ioctl already does a multi-byte copy, we can now copy arbitrary lengths of bytes into kernel memory. One method of turning an arbitrary kernel memory write into a privilege escalation is to overwrite a kernel function pointer, and trigger that function. Based on the exploit for CVE-2010-3081, I opted to overwrite the security_ops
function pointer table. Their use of msg_queue_msgctl
wasn t very good for the general case since it s near the end of the table and its offset would depend on kernel versions. Initially I opted for getcap
, but in the end used ptrace_traceme
, both of which are very near the top the security_ops
structure. (Though I need share credit here with Dan Rosenberg as we were working together on improving the reliability of the security_ops
overwrite method. He used the same approach for his excellent RDS exploit.)
Here are the steps for one way of taking an arbitrary kernel memory write and turning it into a root escalation:
security_ops
with default_security_ops
, which will revert the LSM back to the capabilities-only security operations. This, however, means we can calculate where cap_ptrace_traceme
is.default_security_ops->ptrace_traceme
to point to our supplied function that will actually perform the privilege escalation (thanks to Brad Spengler for his code from Enlightenment).ptrace(PTRACE_TRACEME, 0, NULL, NULL)
).default_security_ops->ptrace_traceme
to point to cap_ptrace_traceme
so the next caller doesn t Oops the system (since userspace memory will be remapped).cap_getcap
(which is pretty unstable, so you might want to switch it to use ptrace_traceme
), and as a stand-alone memory writer.
Conclusions: Keep auditing the kernel for more arbitrary writes; I think there are still many left. Reduce the exploitation surface within the kernel itself (which PaX and grsecurity have been doing for a while now), specifically:
fops
, IDT
, security_ops
, etc) to be writable. These should all be marked correctly, with inline code exceptions being made for updating the global pointers to those tables, leaving the pointer read-only after it gets set. This would stop this particular exploit above, but there are still plenty more targets./dev/.udev/rules.d
directory during initial boot kept this vulnerability exposure pretty well minimized, the fact that udev events can be triggered at will made it pretty bad too. If udev had already been restarted, an attacker didn t have to wait at all, nor have physical access to the system.
While it is generally understood that udev events are related to hardware, it s important to keep in mind that it also sends events on module loads, and module loads can happen on demand from unprivileged users. For example, say you want to send an X.25 packet, when you call socket(AF_X25, SOCK_STREAM)
, the kernel will go load net-pf-9
, which modules.alias
lists as the x25
module. And once loaded, udev sends a module event.
(Which, by the way, should serve as a reminder to people to block module loading if you can.)
So, as I mentioned, here s yet another exploit for the mountall vulnerability: mountall-CVE-2010-2961.py. It writes to the vulnerable udev rule file and then attempts to trigger udev immediately by walking a list of possible socket()
AF_*
types.
Next.